ECE5725 Final project | May 13, 2024
By Jason Heller (jmh469) & Noah Abramson (na325)
We set out to create an arcade-style embedded system that allows a user to play Tetris, powered off of a Raspberry Pi 4. Rather than have the game displayed on typical screen, we opted to integrate a 32x64 LED matrix to display the game to the player. The player interacts with the game through arcade buttons housed in a 3D printed enclosure to hold all of the electronics. The game is implemented from scratch, allowing for a player to quick-drop, save pieces and shoot for a high score!
The design of our progress mainly consisted of two, fairly separate efforts. One was creating the game of Tetris in Python, and the other being integrating the hardware we wanted to use in tandem with our software.
Software Design
For the software running Tetris to work best with the hardware, and so we could implement both the software and hardware at the same time, we decided to have the two parts of the system communicate with one another throuh a 2D matrix representing the Tetris grid. The game implementation would handle the logic that controlled what was in this 2D array, while the hardware would poll this array to know what to display on the screen. Also, to make the development easier, most of the game was created on a Windows machine, running the game in a terminal, with a print function that printed out the contents of the grid array. This freed our Raspberry Pi up to be used to integrate the LED matrix (discussed in the 'Hardware Design' section).
We slowly built up the implementation of the game, starting with a single piece. We created a MovingPiece class that held all of the important information that we would need to control a piece that is falling down. This included the piece's location, color, rotation, shape, if a piece was saved, and a few other attributes used to improve the feel of the game (discussed later). For the position of a piece, we decided to have one of the blocks that made up a piece be its "main" block, whose position was the whole piece's position.
We also had to implement similar checks to the rotations to the moving left and right of pieces. We would check if the blocks of the grid to the left or right of all of the blocks included in the falling piece were either filled by another piece, or went off the grid. If either of these were true, we stopped the piece from moving to the side, otherwise we would move the piece. With all of the movement of a falling piece, we could move on to implementing the larger game logic.
For the decision of what pieces were next, we created a function that generated a random piece. With this, we populated a list of these pieces which the first element would be the next piece. This is so that we could display the next pieces coming up to the player while they are playing. Also, we had to implement the clearing of pieces when a piece dropped. We did this by checking if a whole row was full of blocks every time a piece was placed into the grid 2D array, and if it was full, it was removed and points were added to the player's score. If they cleared a single line, they would gain 40 points, 2 lines gave 100 points, 3 lines gave 300 points, and a full 4-line tetris gives 1200 points. With these scores, we would increase the speed of the game when the user hit a score threshold.
Once all of these things were implemented, we could work on adding a few quality of life improvements to the game to make it more usable. We added a little delay in dropping a piece, so that a player could slide a piece right before it is placed, which a lot of Tetris games do. Also, we added a counter of spins right before a piece was placed, so that a player could stall the dropping of a piece by spinning it. This gives the players a little bit of leeway while playing, which makes the game much more enjoyable. We also added a repeating loop of gameplay that plays before the player plays the game. This animation takes in pieces' locations and moves them accordingly to play the game and give an interesting visual while the game isnt being played.
Finally, we added a state machine to the code so that we could step through start up, playing the game, losing, and resetting the game easily. We saved it in a value to keep track of where we were, and based on what happened in the game, we would transition between states.
With the game implemented, all that was left to add to the code was the code that integrated with the hardware. We utilized 5 GPIO pins for the arcade buttons. To test these, we assigned one of the movement functions of the pieces to the buttons with a callback, and were able to play the game with the buttons! We did end up adding debouncing to these callbacks, as our buttons weren't the highest quality, and we added extra debouncing time to the drop button so a player wouldn't accidentally drop two pieces, as that is very frusturating.
Hardware Design
On the non-software side of the project, we had to integrate the Adafruit 32x64 LED matrix to work with our project. Much of this work was trying to find out how to properly wire the matrix, and control it reliably. Our biggest problem that we encountered during this time, was that there was not much documentation on how to use the screen, but also that the first matrix that we tried to use was broken. When we ran code on it, the shapes were very blurry and we weren't sure if it was that we were controlling it wrong, or the screen was broken. Once we did have a working screen, we were able to use the libraries provided to confirm that it worked by displaying an image on the screen.
NOAH WRITE THE WIRING OF THE SCREEN HERE. We also wired our buttons to the GPIOs using 1k current-limiting resistors in series to protect the Raspberry Pi.
To display our game on the LED matrix, we had to create a few functions to interface with the matrix. The library gave us a "draw line" function and we used that to create all of our drawings. We created a "draw block" function that drew a 2x2 square on the screen at a given location on the game board, so that we could translate our 2D array into the matrix. Then, as we did not have a reliable way to write text (the library had a text function but it was rotated the wrong way), we coded in the letters and numbers we would want for the UI and the score using the draw line function. This was the last of the code that we needed to add to get the game working.
With the LED matrix working, and us able to print our game to it, we could then move on to the enclosure around our electronics. We cadded a box in Autodesk Inventor, in which the LED matrix could rest in a hole cut out for it, the Raspberry Pi could be velcroed to the back, and the buttons could be slotted into holes made for them. Because the box was slightly longer than 14 inches long, the box had to be split into two pieces to be pintable.
All in all, mostly everything in our project worked out how we envisioned it at the start. We didn't originally plan to have a 3D printed enclosure, so we are very happy with having enough time to add that, as it made the whole project really come together. Originally, we had an idea to add sound and visual effects to the project, but we didn't end up having time for that, and didn't feel like it was necessary to the project as a whole.
In our project, we were able to achieve a fully functional implmentation of Tetris with all of the related electronics working together in an embedded system. We discovered that integration with the LED matrix was not as easy as we initially anticipated, and caused us to need a lot of hard-coding to get the look that we wanted. There are still some quirks with the system, like the LED matrix sometimes has flashes of brightness, but we actually think that is matches the old-timey videogame asthetic that Tetris comes from.
With more time, there would be a good amount of work that we could do the improve the system. For one, figuring out why the screen has flashes of color would be interesting. Also, discovering a better way to deal with text would be nice, and save a lot of time if we wanted to do more text-heavy UI. Right now we only added the characters that we knew that we would need, and it took a long time. Also, with the system that we have, we could add different games to the system, like space invaders (like Noah's dad wanted). Also, it would be fun to implement a leaderboard that keeps peoples' scores after they are done, but adding a way for then to type in their names would take a fair amount of time.
jmh469@cornell.edu
Coded Tetris implementation, designed electronics enclosure
na325@cornell.edu
Integrated LED matrix and translations from python implementation to LED matrix
// Hello World.c
int main(){
printf("Hello World.\n");
}